/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.cadoc.cqe;

import java.util.HashMap;
import java.util.Map;

import org.cadoc.cqe.logic.Pattern;
import org.cadoc.cqe.logic.Predicate;
import org.cadoc.cqe.logic.Variable;
import org.cadoc.cqe.logic.predicate.CallsPredicate;
import org.cadoc.cqe.logic.predicate.DeclaresFieldPredicate;
import org.cadoc.cqe.logic.predicate.DeclaresOperationPredicate;
import org.cadoc.cqe.logic.predicate.ExtendsPredicate;
import org.cadoc.cqe.logic.predicate.ImplementsPredicate;
import org.cadoc.cqe.logic.predicate.ModifierPredicate;

/**
 * @author Andre Albert &lt;andre.albert82@googlemail.com&gt;
 */
public class PatternAnalyzerImpl implements PatternAnalyzer {

	public Map<? extends Variable, Integer> analyze(Pattern pattern) {
		Map<Variable, Integer> varFactVectors = new HashMap<Variable, Integer>();

		Integer subjectFactVector = null;
		Integer constraintFactVector = null;

		for (Predicate<? extends Variable, ? extends Variable> pred : pattern.predicates()) {
			subjectFactVector = varFactVectors.get(pred.getSubjectVariable());
			if (subjectFactVector == null) {
				subjectFactVector = new Integer(0);
				varFactVectors.put(pred.getSubjectVariable(), subjectFactVector);
			}
			constraintFactVector = varFactVectors.get(pred.getConstraintVariable());
			if (constraintFactVector == null) {
				constraintFactVector = new Integer(0);
				varFactVectors.put(pred.getConstraintVariable(), constraintFactVector);
			}
			
			if (pred instanceof CallsPredicate) {
				handlePredicate((CallsPredicate) pred, subjectFactVector, constraintFactVector);
			} else if (pred instanceof ExtendsPredicate) {
				handlePredicate((ExtendsPredicate) pred, subjectFactVector, constraintFactVector);
			} else if (pred instanceof DeclaresFieldPredicate) {
				handlePredicate((DeclaresFieldPredicate) pred, subjectFactVector, constraintFactVector);
			} else if (pred instanceof DeclaresOperationPredicate) {
				handlePredicate((DeclaresOperationPredicate) pred, subjectFactVector, constraintFactVector);
			} else if (pred instanceof ImplementsPredicate) {
				handlePredicate((ImplementsPredicate) pred, subjectFactVector, constraintFactVector);
			} else if (pred instanceof ModifierPredicate) {
				handlePredicate((ModifierPredicate) pred, subjectFactVector, constraintFactVector);
			}
		}

		return varFactVectors;
	}

	private void handlePredicate(CallsPredicate pred, Integer subjectFactVector, Integer constraintFactVector) {

		subjectFactVector |= FactVector.IS_OPERATION.value();
		subjectFactVector |= FactVector.CALLS.value();

		constraintFactVector |= FactVector.IS_OPERATION.value();
		constraintFactVector |= FactVector.CALLED.value();
	}

	private void handlePredicate(ExtendsPredicate pred, Integer subjectFactVector,
			Integer constraintFactVector) {

		subjectFactVector |= FactVector.IS_TYPE.value();
		subjectFactVector |= FactVector.EXTENDS.value();

		constraintFactVector |= FactVector.IS_TYPE.value();
		constraintFactVector |= FactVector.EXTENDED.value();
	}

	private void handlePredicate(DeclaresFieldPredicate pred, Integer subjectFactVector,
			Integer constraintFactVector) {

		subjectFactVector |= FactVector.IS_TYPE.value();
		constraintFactVector |= FactVector.IS_ELEMENT.value();
	}

	private void handlePredicate(DeclaresOperationPredicate pred, Integer subjectFactVector,
			Integer constraintFactVector) {

		subjectFactVector |= FactVector.IS_TYPE.value();
		constraintFactVector |= FactVector.IS_OPERATION.value();
	}
	
	private void handlePredicate(ImplementsPredicate pred, Integer subjectFactVector,
			Integer constraintFactVector) {

		subjectFactVector |= FactVector.IS_TYPE.value();
		subjectFactVector |= FactVector.IMPLEMENTS.value();
		
		constraintFactVector |= FactVector.IS_TYPE.value();
	}
	
	private void handlePredicate(ModifierPredicate pred, Integer subjectFactVector,
			Integer constraintFactVector) {

		switch(pred.getConstraintVariable().getCurrentBinding()){
			case ABSTRACT: {
				subjectFactVector |= FactVector.ABSTRACT.value();
				break;
			}
			case FINAL: {
				subjectFactVector |= FactVector.FINAL.value();
				break;
			}
			case PRIVATE: {
				subjectFactVector |= FactVector.PRIVATE.value();
				break;
			}
			case PUBLIC: {
				subjectFactVector |= FactVector.PUBLIC.value();
				break;
			}
			case STATIC: {
				subjectFactVector |= FactVector.STATIC.value();
				break;
			}
			case SYNCHRONIZED: {
				subjectFactVector |= FactVector.SYNCHRONIZED.value();
				break;
			}
			case TRANSIENT: {
				subjectFactVector |= FactVector.TRANSIENT.value();
				break;
			}
		}
	}
}
